/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.domain;

import org.braintrain.domain.types.NavigationType;
import org.braintrain.services.util.contentBaseVisitor.ContentBaseVisitor;
import org.hibernate.annotations.IndexColumn;

import javax.persistence.*;
import java.util.*;

/**
 * Stores the data associated with a test.  This includes many options from logging
 * ("book marking") the given test to email a certificate when a test was completed.
 * Also, stores references to all the Question Objects.
 * 
 * @author John Ericksen
 *
 */

@Entity
@Table( name="braintrain_test" )
public class TestData extends ContentBase{

    private static final long serialVersionUID = 8821764503168398439L;

    private List<Question> questions = new ArrayList<Question>();

    private boolean emailCertificate;
    private boolean randomizeQuestions;
    private boolean randomizeAnswers;
    private boolean activateNextButton;

    private int numberOfQuestionsToUse = 0;
    private int numberCorrectToPass = 0;
    private int numberStartAt = 1;

    private boolean clearIncorrectAnswer;

    private boolean logTestResults;

    private List<TestLog> testLogs = new ArrayList<TestLog>();
    private String instructions;

    @Column(length=4000)
    public String getInstructions() {
        return instructions;
    }

    public void setInstructions(String instructions) {
        this.instructions = instructions;
    }

    public boolean isLogTestResults() {
        return logTestResults;
    }

    public void setLogTestResults(boolean logTestResults) {
        this.logTestResults = logTestResults;
    }

    public boolean isEmailCertificate() {
        return emailCertificate;
    }

    public void setEmailCertificate(boolean emailCertificate) {
        this.emailCertificate = emailCertificate;
    }

    public int getNumberCorrectToPass() {
        return numberCorrectToPass;
    }

    public void setNumberCorrectToPass(int numberCorrectToPass) {
        this.numberCorrectToPass = numberCorrectToPass;
    }

    public int getNumberOfQuestionsToUse() {
        return numberOfQuestionsToUse;
    }

    public void setNumberOfQuestionsToUse(int numberOfQuestionsToUse) {
        this.numberOfQuestionsToUse = numberOfQuestionsToUse;
    }

    public boolean isRandomizeAnswers() {
        return randomizeAnswers;
    }

    public void setRandomizeAnswers(boolean randomizeAnswers) {
        this.randomizeAnswers = randomizeAnswers;
    }

    public boolean isRandomizeQuestions() {
        return randomizeQuestions;
    }

    public void setRandomizeQuestions(boolean randomizeQuestions) {
        this.randomizeQuestions = randomizeQuestions;
    }

    @OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn( name="test_id" )
    @IndexColumn(name = "questionOrder", base=0)
    public List<Question> getQuestions() {
        return questions;
    }

    public void setQuestions(List<Question> multipleChoiceQuestions) {
        this.questions = multipleChoiceQuestions;
    }

    @Transient
    public boolean isTest() {
        return true;
    }



    public boolean isClearIncorrectAnswer() {
        return clearIncorrectAnswer;
    }

    public void setClearIncorrectAnswer(boolean clearIncorrectAnswer) {
        this.clearIncorrectAnswer = clearIncorrectAnswer;
    }

    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumn( name="test_id")
    public List<TestLog> getTestLogs() {
        return testLogs;
    }

    public void setTestLogs(List<TestLog> testLogs) {
        this.testLogs = testLogs;
    }

    public int getNumberStartAt() {
        return numberStartAt;
    }

    public void setNumberStartAt(int numberStartAt) {
        this.numberStartAt = numberStartAt;
    }

    public boolean isActivateNextButton() {
        return activateNextButton;
    }

    public void setActivateNextButton(boolean activateNextButton) {
        this.activateNextButton = activateNextButton;
    }
    
    @Transient
    public boolean isBookmarked(User user) {

        boolean bookmarked = false;

        if(isLogTestResults())
        {
            List<TestLog> log = getTestLogs();
            for(TestLog entry: log)
            {
                if(entry.getUser().equals(user))
                {
                    bookmarked = true;
                }
            }
        }

        return bookmarked;
    }

    @Transient
    public boolean isBookmarkable()
    {
        return isLogTestResults();
    }



    public ContentBase copy() {
        TestData copy = new TestData();

        copy = (TestData)super.copy(copy);

        copy.setEmailCertificate(this.isEmailCertificate());
        copy.setRandomizeQuestions(this.isRandomizeQuestions());
        copy.setRandomizeAnswers(this.isRandomizeAnswers());
        copy.setNumberOfQuestionsToUse(this.getNumberOfQuestionsToUse());
        copy.setNumberCorrectToPass(this.getNumberCorrectToPass());
        copy.setNumberStartAt(this.getNumberStartAt());
        copy.setClearIncorrectAnswer(this.isClearIncorrectAnswer());
        copy.setLogTestResults(this.isLogTestResults());
        copy.setInstructions(this.getInstructions());
        copy.setActivateNextButton(this.isActivateNextButton());

        for(Question question: this.getQuestions())
        {
            copy.getQuestions().add(question.copy());
        }

        copy.getTestLogs().addAll(this.getTestLogs());

        copy.setVersion(this.getVersion());

        return copy;
    }

    public void clearIncorrectAnswers(){
        if(getQuestions() == null)
        {
            setQuestions(new ArrayList<Question>());
        }
        for(Question q: getQuestions())
        {
            if(clearIncorrectAnswer && !q.isCorrect())
            {
                q.setUserAnswer(null);

                if(q.getAnswers() == null)
                {
                    q.setAnswers(new ArrayList<Answer>());
                }
                for(Answer a: q.getAnswers())
                {
                    a.setUserAnswer(false);
                }
            }
        }
    }

    public void resetTest() {
        if(getQuestions() == null)
        {
            setQuestions(new ArrayList<Question>());
        }
        for(Question q: getQuestions())
        {
            q.resetQuestion();
        }

    }

    public void setupTest()
    {
        //Randomize questions
        if(randomizeQuestions)
        {
            Collections.shuffle(questions);
        }
        //Randomize answers
        if(randomizeAnswers)
        {
            for(Question question: questions)
            {
                Collections.shuffle(question.getAnswers());
            }
        }
        //Number of questions to use
        if(questions != null)
        {
            int numberOfQuestionsToRemove = questions.size() - numberOfQuestionsToUse;
            Random rand = new Random(System.currentTimeMillis());
            for(int i = 0; i < numberOfQuestionsToRemove; i++)
            {
                questions.remove(rand.nextInt(questions.size()));
            }
        
            //remove empty answers
            Iterator<Question> qiter = questions.iterator();
            while(qiter.hasNext())
            {
                Question question = qiter.next();
                Iterator<Answer> aiter = question.getAnswers().iterator();
                Set<Answer> removeAnswers = new HashSet<Answer>();
                while(aiter.hasNext())
                {
                    Answer a = aiter.next();
                    if(a.getAnswer() == null || a.getAnswer().trim().length() == 0)
                    {
                        removeAnswers.add(a);
                    }
                    else if (!isRandomizeAnswers())
                    {
                        removeAnswers.clear();
                    }
                }
                
                question.getAnswers().removeAll(removeAnswers);
            }
        }
   
    }

    public int gradeTest(User user) {
        int correct = 0;
        for(Question question: questions)
        {
            if(question.gradeQuestion(user))
            {
                ++correct;
            }
        }
        return correct;
    }
    
    @Transient
    public Set<AnswerLog> getAnswerLog(){
        Set<AnswerLog> log = new TreeSet<AnswerLog>();
        
        for(Question question: questions)
        {
            log.addAll(question.getAnswerLog());
        }
        
        return log;
    }

    @Transient
    public String toString()
    {
        StringBuffer buff = new StringBuffer();

        buff.append("TEST: " + getName() + "\n");

        for(Question q: getQuestions())
        {
            buff.append(q.toString());
        }

        return buff.toString();
    }
    @Transient
    public void resetIndexes()
    {
        int index = 0;
        for(Question q: getQuestions())
        {
            q.setQuestionOrder(index++);
            q.resetIndexes();
        }
    }

    @Transient
    public NavigationType getType()
    {
        return NavigationType.Test;
    }

    public void accept(ContentBaseVisitor visitor)
    {
        visitor.visit(this);
    }
}